#include <Misra/Std/Container/Str.h>
#include <Misra/Std/Container/BitVec.h>
#include <Misra/Std/Io.h>
#include <Misra/Std/Log.h>
#include <math.h>   // For INFINITY and NAN
#include <string.h> // For strlen
#include <stdio.h>
#include <Misra/Types.h>

// Include test utilities
#include "../Util/TestRunner.h"

// Function prototypes
bool test_basic_formatting(void);
bool test_string_formatting(void);
bool test_integer_decimal_formatting(void);
bool test_integer_hex_formatting(void);
bool test_integer_binary_formatting(void);
bool test_integer_octal_formatting(void);
bool test_float_basic_formatting(void);
bool test_float_precision_formatting(void);
bool test_float_special_values(void);
bool test_width_alignment_formatting(void);
bool test_multiple_arguments(void);
bool test_error_handling(void);
bool test_char_formatting(void);
bool test_bitvec_formatting(void);

// Test basic formatting features
bool test_basic_formatting(void) {
    WriteFmt("Testing basic formatting\n");

    Str  output  = StrInit();
    bool success = true;

    // Test empty format string
    StrWriteFmt(&output, "");
    success = success && (output.length == 0);
    StrClear(&output);

    // Test literal text
    StrWriteFmt(&output, "Hello, world!");
    success = success && (ZstrCompare(output.data, "Hello, world!") == 0);
    StrClear(&output);

    // Test escaped braces
    StrWriteFmt(&output, "{{Hello}}");
    success = success && (ZstrCompare(output.data, "{Hello}") == 0);
    StrClear(&output);

    // Test double escaped braces
    StrWriteFmt(&output, "{{{{");
    success = success && (ZstrCompare(output.data, "{{") == 0);

    StrDeinit(&output);
    return success;
}

// Test string formatting
bool test_string_formatting(void) {
    WriteFmt("Testing string formatting\n");

    Str  output  = StrInit();
    bool success = true;

    // Test basic string
    const char *str = "Hello";
    StrWriteFmt(&output, "{}", str);
    success = success && (ZstrCompare(output.data, "Hello") == 0);
    StrClear(&output);

    // Test empty string
    const char *empty = "";
    StrWriteFmt(&output, "{}", empty);
    success = success && (output.length == 0);
    StrClear(&output);

    // Test string with width and alignment
    StrWriteFmt(&output, "{>10}", str);
    success = success && (ZstrCompare(output.data, "     Hello") == 0);
    StrClear(&output);

    StrWriteFmt(&output, "{<10}", str);
    success = success && (ZstrCompare(output.data, "Hello     ") == 0);
    StrClear(&output);

    StrWriteFmt(&output, "{^10}", str);
    success = success && (ZstrCompare(output.data, "  Hello   ") == 0);
    StrClear(&output);

    // Test Str object
    Str s = StrInitFromZstr("World");
    StrWriteFmt(&output, "{}", s);
    success = success && (ZstrCompare(output.data, "World") == 0);
    StrDeinit(&s);

    StrDeinit(&output);
    return success;
}

// Test decimal integer formatting
bool test_integer_decimal_formatting(void) {
    WriteFmt("Testing integer decimal formatting\n");

    Str  output  = StrInit();
    bool success = true;

    // Test signed integers
    i8 i8_val = -42;
    StrWriteFmt(&output, "{}", i8_val);
    success = success && (ZstrCompare(output.data, "-42") == 0);
    StrClear(&output);

    i16 i16_val = -1234;
    StrWriteFmt(&output, "{}", i16_val);
    success = success && (ZstrCompare(output.data, "-1234") == 0);
    StrClear(&output);

    i32 i32_val = -123456;
    StrWriteFmt(&output, "{}", i32_val);
    success = success && (ZstrCompare(output.data, "-123456") == 0);
    StrClear(&output);

    i64 i64_val = -1234567890LL;
    StrWriteFmt(&output, "{}", i64_val);
    success = success && (ZstrCompare(output.data, "-1234567890") == 0);
    StrClear(&output);

    // Test unsigned integers
    u8 u8_val = 42;
    StrWriteFmt(&output, "{}", u8_val);
    success = success && (ZstrCompare(output.data, "42") == 0);
    StrClear(&output);

    u16 u16_val = 1234;
    StrWriteFmt(&output, "{}", u16_val);
    success = success && (ZstrCompare(output.data, "1234") == 0);
    StrClear(&output);

    u32 u32_val = 123456;
    StrWriteFmt(&output, "{}", u32_val);
    success = success && (ZstrCompare(output.data, "123456") == 0);
    StrClear(&output);

    u64 u64_val = 1234567890ULL;
    StrWriteFmt(&output, "{}", u64_val);
    success = success && (ZstrCompare(output.data, "1234567890") == 0);
    StrClear(&output);

    // Test edge cases
    i8 i8_max = 127;
    StrWriteFmt(&output, "{}", i8_max);
    success = success && (ZstrCompare(output.data, "127") == 0);
    StrClear(&output);

    i8 i8_min = -128;
    StrWriteFmt(&output, "{}", i8_min);
    success = success && (ZstrCompare(output.data, "-128") == 0);
    StrClear(&output);

    u8 u8_max = 255;
    StrWriteFmt(&output, "{}", u8_max);
    success = success && (ZstrCompare(output.data, "255") == 0);
    StrClear(&output);

    u8 u8_min = 0;
    StrWriteFmt(&output, "{}", u8_min);
    success = success && (ZstrCompare(output.data, "0") == 0);

    StrDeinit(&output);
    return success;
}

// Test hexadecimal formatting
bool test_integer_hex_formatting(void) {
    WriteFmt("Testing integer hexadecimal formatting\n");

    Str  output  = StrInit();
    bool success = true;

    u32 val = 0xDEADBEEF;
    StrWriteFmt(&output, "{x}", val);
    success = success && (ZstrCompare(output.data, "0xdeadbeef") == 0);
    StrClear(&output);

    StrWriteFmt(&output, "{X}", val);
    success = success && (ZstrCompare(output.data, "0xDEADBEEF") == 0);

    StrDeinit(&output);
    return success;
}

// Test binary formatting
bool test_integer_binary_formatting(void) {
    WriteFmt("Testing integer binary formatting\n");

    Str  output  = StrInit();
    bool success = true;

    u8 val = 0xA5; // 10100101 in binary
    StrWriteFmt(&output, "{b}", val);
    success = success && (ZstrCompare(output.data, "0b10100101") == 0);

    StrDeinit(&output);
    return success;
}

// Test octal formatting
bool test_integer_octal_formatting(void) {
    WriteFmt("Testing integer octal formatting\n");

    Str  output  = StrInit();
    bool success = true;

    u16 val = 0777;
    StrWriteFmt(&output, "{o}", val);
    success = success && (ZstrCompare(output.data, "0o777") == 0);

    StrDeinit(&output);
    return success;
}

// Test basic floating point formatting
bool test_float_basic_formatting(void) {
    WriteFmt("Testing basic floating point formatting\n");

    Str  output  = StrInit();
    bool success = true;

    f32 f32_val = 3.14159f;
    StrWriteFmt(&output, "{}", f32_val);
    success = success && (ZstrCompare(output.data, "3.141590") == 0);
    StrClear(&output);

    f64 f64_val = 2.71828;
    StrWriteFmt(&output, "{}", f64_val);
    success = success && (ZstrCompare(output.data, "2.718280") == 0);

    StrDeinit(&output);
    return success;
}

// Test floating point precision
bool test_float_precision_formatting(void) {
    WriteFmt("Testing floating point precision formatting\n");

    Str  output  = StrInit();
    bool success = true;

    f64 val = 3.14159265359;

    // Test different precisions
    StrWriteFmt(&output, "{.2}", val);
    success = success && (ZstrCompare(output.data, "3.14") == 0);
    StrClear(&output);

    StrWriteFmt(&output, "{.0}", val);
    success = success && (ZstrCompare(output.data, "3") == 0);
    StrClear(&output);

    StrWriteFmt(&output, "{.10}", val);
    success = success && (ZstrCompare(output.data, "3.1415926536") == 0);

    StrDeinit(&output);
    return success;
}

// Test special floating point values
bool test_float_special_values(void) {
    WriteFmt("Testing special floating point values\n");

    Str  output  = StrInit();
    bool success = true;

    // Test infinity
    f64 pos_inf = INFINITY;
    StrWriteFmt(&output, "{}", pos_inf);
    success = success && (ZstrCompare(output.data, "inf") == 0);
    StrClear(&output);

    f64 neg_inf = -INFINITY;
    StrWriteFmt(&output, "{}", neg_inf);
    success = success && (ZstrCompare(output.data, "-inf") == 0);
    StrClear(&output);

    // Test NaN
    f64 nan_val = NAN;
    StrWriteFmt(&output, "{}", nan_val);
    success = success && (ZstrCompare(output.data, "nan") == 0);

    StrDeinit(&output);
    return success;
}

// Test width and alignment formatting
bool test_width_alignment_formatting(void) {
    WriteFmt("Testing width and alignment formatting\n");

    Str  output  = StrInit();
    bool success = true;

    // Test with integers
    i32 val = 42;
    StrWriteFmt(&output, "{5}", val);
    success = success && (ZstrCompare(output.data, "   42") == 0);
    StrClear(&output);

    StrWriteFmt(&output, "{<5}", val);
    success = success && (ZstrCompare(output.data, "42   ") == 0);
    StrClear(&output);

    StrWriteFmt(&output, "{^5}", val);
    success = success && (ZstrCompare(output.data, " 42  ") == 0);
    StrClear(&output);

    // Test with strings
    const char *str = "abc";
    StrWriteFmt(&output, "{5}", str);
    success = success && (ZstrCompare(output.data, "  abc") == 0);
    StrClear(&output);

    StrWriteFmt(&output, "{<5}", str);
    success = success && (ZstrCompare(output.data, "abc  ") == 0);
    StrClear(&output);

    StrWriteFmt(&output, "{^5}", str);
    success = success && (ZstrCompare(output.data, " abc ") == 0);

    StrDeinit(&output);
    return success;
}

// Test multiple arguments
bool test_multiple_arguments(void) {
    WriteFmt("Testing multiple arguments\n");

    Str  output  = StrInit();
    bool success = true;

    const char *hello = "Hello";
    i32         num   = 42;
    f64         pi    = 3.14;

    StrWriteFmt(&output, "{} {} {}", hello, num, pi);
    success = success && (ZstrCompare(output.data, "Hello 42 3.140000") == 0);
    StrClear(&output);

    // Instead of using positional arguments, we'll just reorder the arguments themselves
    StrWriteFmt(&output, "{} {} {}", pi, hello, num);
    success = success && (ZstrCompare(output.data, "3.140000 Hello 42") == 0);

    StrDeinit(&output);
    return success;
}

// Test character formatting specifiers
bool test_char_formatting(void) {
    WriteFmt("Testing character formatting specifiers\n");

    Str  output  = StrInit();
    bool success = true;

    // Test mixed case string with :c (preserve case)
    const char *mixed_case = "MiXeD CaSe";
    StrWriteFmt(&output, "{c}", mixed_case);
    success = success && (ZstrCompare(output.data, "MiXeD CaSe") == 0);
    StrClear(&output);

    // Test mixed case string with :a (lowercase)
    StrWriteFmt(&output, "{a}", mixed_case);
    success = success && (ZstrCompare(output.data, "mixed case") == 0);
    StrClear(&output);

    // Test mixed case string with :A (uppercase)
    StrWriteFmt(&output, "{A}", mixed_case);
    success = success && (ZstrCompare(output.data, "MIXED CASE") == 0);
    StrClear(&output);

    // Test with Str object
    Str s = StrInitFromZstr("MiXeD CaSe");

    // Test with :c (preserve case)
    StrWriteFmt(&output, "{c}", s);
    success = success && (ZstrCompare(output.data, "MiXeD CaSe") == 0);
    StrClear(&output);

    // Test with :a (lowercase)
    StrWriteFmt(&output, "{a}", s);
    success = success && (ZstrCompare(output.data, "mixed case") == 0);
    StrClear(&output);

    // Test with :A (uppercase)
    StrWriteFmt(&output, "{A}", s);
    success = success && (ZstrCompare(output.data, "MIXED CASE") == 0);
    StrClear(&output);

    // Test with character values (u8)
    u8 upper_char = 'M';
    u8 lower_char = 'm';

    // Test uppercase char with :c (preserve case)
    StrWriteFmt(&output, "{c}", upper_char);
    success = success && (ZstrCompare(output.data, "M") == 0);
    StrClear(&output);

    // Test uppercase char with :a (lowercase)
    StrWriteFmt(&output, "{a}", upper_char);
    success = success && (ZstrCompare(output.data, "m") == 0);
    StrClear(&output);

    // Test lowercase char with :A (uppercase)
    StrWriteFmt(&output, "{A}", lower_char);
    success = success && (ZstrCompare(output.data, "M") == 0);
    StrClear(&output);

    // Test with u16 (containing ASCII values)
    u16 u16_value = ('A' << 8) | 'B'; // AB in big-endian

    // Test u16 with :c (preserve case)
    StrWriteFmt(&output, "{c}", u16_value);
    success = success && (output.length == 2 && output.data[0] == 'A' && output.data[1] == 'B');
    StrClear(&output);

    // Test u16 with :a (lowercase)
    StrWriteFmt(&output, "{a}", u16_value);
    success = success && (output.length == 2 && output.data[0] == 'a' && output.data[1] == 'b');
    StrClear(&output);

    // Test u16 with :A (uppercase)
    StrWriteFmt(&output, "{A}", u16_value);
    success = success && (output.length == 2 && output.data[0] == 'A' && output.data[1] == 'B');
    StrClear(&output);

    // Test with i16 (containing ASCII values)
    i16 i16_value = ('C' << 8) | 'd'; // Cd in big-endian

    // Test i16 with :c (preserve case)
    StrWriteFmt(&output, "{c}", i16_value);
    success = success && (output.length == 2 && output.data[0] == 'C' && output.data[1] == 'd');
    StrClear(&output);

    // Test i16 with :a (lowercase)
    StrWriteFmt(&output, "{a}", i16_value);
    success = success && (output.length == 2 && output.data[0] == 'c' && output.data[1] == 'd');
    StrClear(&output);

    // Test i16 with :A (uppercase)
    StrWriteFmt(&output, "{A}", i16_value);
    success = success && (output.length == 2 && output.data[0] == 'C' && output.data[1] == 'D');
    StrClear(&output);

    // Test with u32 (containing ASCII values)
    u32 u32_value = ('E' << 24) | ('f' << 16) | ('G' << 8) | 'h'; // EfGh in big-endian

    // Test u32 with :c (preserve case)
    StrWriteFmt(&output, "{c}", u32_value);
    success = success && (output.length == 4 && output.data[0] == 'E' && output.data[1] == 'f' &&
                          output.data[2] == 'G' && output.data[3] == 'h');
    StrClear(&output);

    // Test u32 with :a (lowercase)
    StrWriteFmt(&output, "{a}", u32_value);
    success = success && (output.length == 4 && output.data[0] == 'e' && output.data[1] == 'f' &&
                          output.data[2] == 'g' && output.data[3] == 'h');
    StrClear(&output);

    // Test u32 with :A (uppercase)
    StrWriteFmt(&output, "{A}", u32_value);
    success = success && (output.length == 4 && output.data[0] == 'E' && output.data[1] == 'F' &&
                          output.data[2] == 'G' && output.data[3] == 'H');
    StrClear(&output);

    // Test with i32 (containing ASCII values)
    i32 i32_value = ('I' << 24) | ('j' << 16) | ('K' << 8) | 'l'; // IjKl in big-endian

    // Test i32 with :c (preserve case)
    StrWriteFmt(&output, "{c}", i32_value);
    success = success && (output.length == 4 && output.data[0] == 'I' && output.data[1] == 'j' &&
                          output.data[2] == 'K' && output.data[3] == 'l');
    StrClear(&output);

    // Test i32 with :a (lowercase)
    StrWriteFmt(&output, "{a}", i32_value);
    success = success && (output.length == 4 && output.data[0] == 'i' && output.data[1] == 'j' &&
                          output.data[2] == 'k' && output.data[3] == 'l');
    StrClear(&output);

    // Test i32 with :A (uppercase)
    StrWriteFmt(&output, "{A}", i32_value);
    success = success && (output.length == 4 && output.data[0] == 'I' && output.data[1] == 'J' &&
                          output.data[2] == 'K' && output.data[3] == 'L');
    StrClear(&output);

    // Test with u64 (containing ASCII values)
    u64 u64_value = ((u64)'M' << 56) | ((u64)'n' << 48) | ((u64)'O' << 40) | ((u64)'p' << 32) | ('Q' << 24) |
                    ('r' << 16) | ('S' << 8) | 't'; // MnOpQrSt in big-endian

    // Test u64 with :c (preserve case)
    StrWriteFmt(&output, "{c}", u64_value);
    success = success && (output.length == 8 && output.data[0] == 'M' && output.data[1] == 'n' &&
                          output.data[2] == 'O' && output.data[3] == 'p' && output.data[4] == 'Q' &&
                          output.data[5] == 'r' && output.data[6] == 'S' && output.data[7] == 't');
    StrClear(&output);

    // Test u64 with :a (lowercase)
    StrWriteFmt(&output, "{a}", u64_value);
    success = success && (output.length == 8 && output.data[0] == 'm' && output.data[1] == 'n' &&
                          output.data[2] == 'o' && output.data[3] == 'p' && output.data[4] == 'q' &&
                          output.data[5] == 'r' && output.data[6] == 's' && output.data[7] == 't');
    StrClear(&output);

    // Test u64 with :A (uppercase)
    StrWriteFmt(&output, "{A}", u64_value);
    success = success && (output.length == 8 && output.data[0] == 'M' && output.data[1] == 'N' &&
                          output.data[2] == 'O' && output.data[3] == 'P' && output.data[4] == 'Q' &&
                          output.data[5] == 'R' && output.data[6] == 'S' && output.data[7] == 'T');
    StrClear(&output);

    // Test with i64 (containing ASCII values)
    i64 i64_value = ((i64)'U' << 56) | ((i64)'v' << 48) | ((i64)'W' << 40) | ((i64)'x' << 32) | ('Y' << 24) |
                    ('z' << 16) | ('1' << 8) | '2'; // UvWxYz12 in big-endian

    // Test i64 with :c (preserve case)
    StrWriteFmt(&output, "{c}", i64_value);
    success = success && (output.length == 8 && output.data[0] == 'U' && output.data[1] == 'v' &&
                          output.data[2] == 'W' && output.data[3] == 'x' && output.data[4] == 'Y' &&
                          output.data[5] == 'z' && output.data[6] == '1' && output.data[7] == '2');
    StrClear(&output);

    // Test i64 with :a (lowercase)
    StrWriteFmt(&output, "{a}", i64_value);
    success = success && (output.length == 8 && output.data[0] == 'u' && output.data[1] == 'v' &&
                          output.data[2] == 'w' && output.data[3] == 'x' && output.data[4] == 'y' &&
                          output.data[5] == 'z' && output.data[6] == '1' && output.data[7] == '2');
    StrClear(&output);

    // Test i64 with :A (uppercase)
    StrWriteFmt(&output, "{A}", i64_value);
    success = success && (output.length == 8 && output.data[0] == 'U' && output.data[1] == 'V' &&
                          output.data[2] == 'W' && output.data[3] == 'X' && output.data[4] == 'Y' &&
                          output.data[5] == 'Z' && output.data[6] == '1' && output.data[7] == '2');

    StrDeinit(&output);
    StrDeinit(&s);
    return success;
}

// Test BitVec formatting
bool test_bitvec_formatting(void) {
    WriteFmt("Testing BitVec formatting\n");

    Str  output  = StrInit();
    bool success = true;

    // Test 1: Basic binary formatting
    BitVec bv1 = BitVecFromStr("10110");
    StrWriteFmt(&output, "{}", bv1);
    success = success && (ZstrCompare(output.data, "10110") == 0);
    StrClear(&output);

    // Test 2: Empty BitVec
    BitVec bv_empty = BitVecInit();
    StrWriteFmt(&output, "{}", bv_empty);
    success = success && (output.length == 0);
    StrClear(&output);

    // Test 3: Hex formatting
    BitVec bv2 = BitVecFromInteger(0xABCD, 16);
    StrWriteFmt(&output, "{x}", bv2);
    success = success && (ZstrCompare(output.data, "0xabcd") == 0);
    StrClear(&output);

    // Test 4: Uppercase hex formatting
    StrWriteFmt(&output, "{X}", bv2);
    success = success && (ZstrCompare(output.data, "0xABCD") == 0);
    StrClear(&output);

    // Test 5: Octal formatting
    BitVec bv3 = BitVecFromInteger(0755, 10);
    StrWriteFmt(&output, "{o}", bv3);
    success = success && (ZstrCompare(output.data, "0o755") == 0);
    StrClear(&output);

    // Test 6: Width and alignment
    StrWriteFmt(&output, "{>10}", bv1);
    success = success && (ZstrCompare(output.data, "     10110") == 0);
    StrClear(&output);

    StrWriteFmt(&output, "{<10}", bv1);
    success = success && (ZstrCompare(output.data, "10110     ") == 0);
    StrClear(&output);

    StrWriteFmt(&output, "{^10}", bv1);
    success = success && (ZstrCompare(output.data, "  10110   ") == 0);
    StrClear(&output);

    // Test 7: Zero value
    BitVec bv_zero = BitVecFromInteger(0, 1);
    StrWriteFmt(&output, "{x}", bv_zero);
    success = success && (ZstrCompare(output.data, "0x0") == 0);
    StrClear(&output);

    StrWriteFmt(&output, "{o}", bv_zero);
    success = success && (ZstrCompare(output.data, "0o0") == 0);
    StrClear(&output);

    // Cleanup
    BitVecDeinit(&bv1);
    BitVecDeinit(&bv_empty);
    BitVecDeinit(&bv2);
    BitVecDeinit(&bv3);
    BitVecDeinit(&bv_zero);
    StrDeinit(&output);
    return success;
}

// // Test error handling
// bool test_error_handling(void) {
//     WriteFmt("Testing error handling\n");

//     // Since we can't directly test error cases without causing program termination,
//     // we'll just report success here. In a real-world scenario, we would need
//     // a more sophisticated approach to test error handling, such as:
//     // 1. Using a separate process that can fail
//     // 2. Capturing logs to verify error messages
//     // 3. Mocking the error handling functions

//     WriteFmt("Note: Error handling tests are skipped as they would cause program termination\n");
//     WriteFmt("In a real-world scenario, these would be tested with a more robust framework\n");

//     // All tests are considered passing since we can't properly test them
//     return true;
// }

// Main function that runs all tests
int main(void) {
    WriteFmt("[INFO] Starting format writer tests\n\n");

    // Array of test functions
    TestFunction tests[] = {
        test_basic_formatting,
        test_string_formatting,
        test_integer_decimal_formatting,
        test_integer_hex_formatting,
        test_integer_binary_formatting,
        test_integer_octal_formatting,
        test_float_basic_formatting,
        test_float_precision_formatting,
        test_float_special_values,
        test_width_alignment_formatting,
        test_multiple_arguments,
        test_char_formatting,
        test_bitvec_formatting
        // test_error_handling
    };

    int total_tests = sizeof(tests) / sizeof(tests[0]);

    // Run all tests using the centralized test driver
    return run_test_suite(tests, total_tests, NULL, 0, "Io.Write");
}
